home *** CD-ROM | disk | FTP | other *** search
/ Mac Magazin/MacEasy 19 / Mac Magazin and MacEasy Magazine CD - Issue 19.iso / Musik & Kunst / Ear Workout 2.1 / source code / ear_chord.cp < prev    next >
Text File  |  1996-01-15  |  36KB  |  1,030 lines

  1. //
  2. //
  3. // shortcomings:
  4. //      -- doesn't play enough 7th chords with missing 3, 5
  5. //    -- since the synthesizer I'm using can only play 4 notes, we're limited to 4-note
  6. //        chords
  7. //    -- Doesn't know much about how to choose among different interpretations of
  8. //        the same chord.  All it knows is that if it has a m7 chord with the
  9. //        3 in the bass, it should reinterpret it as a 6 chord.
  10. //    -- Should give hints about how you could have recognized the previous chord,
  11. //        e.g., it contains an aug triad,...
  12. //    -- when the difficulty level is very high, it still ends up playing quite
  13. //        a few ordinary chords, because it thinks them up in some convoluted
  14. //        way (e.g. m add b6) but then simplify_chord realizes they're
  15. //        more ordinary (maj 7).  On high difficulty levels, should usually
  16. //        reject these chords that turn out to be ordinary, and try again.
  17. //    -- On high difficulty levels, there is a tendency to make chords that are
  18. //        too easy by use of the omitted notes.  Wrote special cases to
  19. //        reject some of these, e.g. 7th chords with omitted roots
  20. //        are really triads.
  21. //    -- choose_chord() operates by choosing a random set of allowed items that is
  22. //        not too dissonant, then simplifying the list of items,
  23. //        then finally making sure all the items on the new, simpler, list
  24. //        are allowed.  Unfortunately, this could result in the program never
  25. //        playing a chord that the user wants it to play. 
  26. //        Alternatives: (1) Could revert to unsimplified form if the
  27. //        simplified form is forbidden, but then they might never learn that,
  28. //        e.g., a dim add b7 is the same as a half-dim.  (2) When they click
  29. //        on a check box to enable or disable an item, could try to make
  30. //        the other check boxes consistent.  E.g. if they have half-dim
  31. //        disabled, dim enabled, and then they enable the b7 added note,
  32. //        we could enabled half-dim as a side-effect.  This would confuse them,
  33. //        though, and they wouldn't be able to go down the list of check boxes
  34. //        and click on them all in a row.
  35. //        Compromise: If simplification results in a
  36. //         reduction of number of items by one or less, and the simplified
  37. //         version uses disabled items, go back to the more complicated
  38. //         version.
  39. //    -- Chords may sound to a musician more like
  40. //        some other conventional chord but with omitted notes, e.g. 
  41. //        B dim add 11 would probably be recognized as G 13 (no 1, no 9
  42. //        no 11).
  43. //    -- Clicking on buttons to get rid of choices sometimes causes a chord
  44. //        symbol to be displayed that's not what they intended. If they get
  45. //        confused, though, they can always wipe out their guess and start over.
  46. //
  47. //
  48. // 29 Nov 95 - Changed fiddle_with_chord_voicing so it includes intervals of 2 and b2
  49. //        more often.
  50.  
  51. #include <string.h>
  52. #include <stdlib.h>
  53. #include <stdio.h>
  54. #include <math.h>
  55.  
  56. #include <OSUtils.h>
  57. #include <QuickDraw.h>
  58. #include <Sound.h>
  59.  
  60. #define NEED_MAC_STUFF 1
  61.  
  62. #include "Ninkasi:C++ util:generic.h"
  63. #include "Ninkasi:C++ util:complete_window.h"
  64. #include "ear_defines.h"
  65. #include "ear_decl.h"
  66. #include "ear_prototypes.h"
  67.  
  68. DEN_MOTHER_T chord_den_mother;
  69.  
  70. void make_about_chord_window();
  71.  
  72.  
  73. void choose_chord(int *item,int *n,double difficulty,int *enabled_list,int n_enabled,
  74.     int min_notes,int max_notes);
  75. void fiddle_with_chord_voicing(int *chord,int n,int inversions_allowed);
  76. int part_num_to_chord_item(int part,char *decoder_string);
  77.  
  78. unsigned int plain_chord_item(unsigned int);
  79. double scale_difficulty(int raw);
  80.  
  81.     //-- had strange problems with sign-extension on & of 4-byte ints!? compiler bug!?
  82. #define IS_CHECK_BOX(chord_item_part) ((((unsigned) (chord_item_part)) & (unsigned) 0x8000L)!=0)
  83. #define PLAIN_CHORD_ITEM(chord_item_part) plain_chord_item((unsigned) (chord_item_part))
  84.  
  85. #define MAXPART 200
  86.     // highest part number in our window
  87.  
  88. extern double fabs();
  89.  
  90. DEN_MOTHER_T about_chord_den_mother;
  91. void make_window(int dlog_resource_id,
  92.         Str255 param_text0,
  93.         Str255 param_text1,
  94.         Str255 param_text2,
  95.         Str255 param_text3,
  96.         DEN_MOTHER_T *den_mother);
  97.  
  98.     void
  99. chord_den_mother(complete_window *my_complete_window)
  100.     {
  101.       //--- decoder string for controls:
  102.       static char *decoder_ptr =
  103.  
  104. "\0ok,play,majt,mint,dimt,augt,sust,b5t,7,m7,maj7,o7,h,b9,9,#9,11,#11,b6,6,a7,amaj7,\
  105. no3,cmajt,cmint,cdimt,caugt,csust,cb5t,c7,cm7,cmaj7,co7,ch,cb9,c9,c#9,c11,c#11,\
  106. cb6,c6,ca7,camaj7,cno3,t1,t2,t3,ic,sc,cl,cho,ar,play_arpeggio,bass,d,lower_difficulty,\
  107. higher_difficulty,guess,help,no1,no5,\
  108. cno1,cno5,inversions,wipe,correct_answer,t4,t5,play_previous";
  109.         //--- has to begin with null so subroutines know it's not really
  110.         //    a Pascal string
  111.         
  112.       static char **decoder_string = &decoder_ptr;
  113.  
  114.       //--- their score:
  115.       static int ntries;
  116.       static int nright;
  117.       //--- keeping track of my current state:
  118.       static int first_time = 1;
  119.       static int they_have_chord_to_consider;
  120.       static int current_icon; 
  121.       static int ready_to_update,need_to_redraw,need_to_redraw_all,pressed_ok,
  122.           play_it,made_about_chord_window;
  123.       //--- parameters that determine what chords I play and how I play them:
  124.       #define TYPICAL_TEMPO_CHORD 45
  125.       #define TYPICAL_TEMPO_ARPEGGIO 200
  126.       static double typical_tempo;
  127.       static int mean_note;
  128.       static double raw_difficulty;
  129.       static double difficulty;
  130.       static int enabled_list[MAXPART];
  131.       static int n_enabled;
  132.       static int chord_or_arpeggio;
  133.       static int min_notes;
  134.       static int max_notes;
  135.       static int inversions_allowed;
  136.       //--- about the current chord:
  137.       static int n_items,item[MAX_CHORD_ITEMS];
  138.       static int n_notes,chord[MAX_CHORD_ITEMS];
  139.       static int duration;
  140.       static int arpeggio_direction;
  141.       static int root;
  142.       //--- about the previous chord:
  143.       //        These are globals because the about_chord den
  144.       //        mother needs to know about them.
  145.       //
  146.       //    ...
  147.       //
  148.       //--- their guess:
  149.       static int guess[MAX_CHORD_ITEMS];
  150.       static int n_guess = 0;
  151.       static int guessed_basic_part = 0;
  152.       
  153.       char nifty_name[100];
  154.       int nifty_index,center,avg,i,right,they_gave_answer,cleared_scores,
  155.           chord_item_pressed,twiddle_check_box,twiddle_radio_buttons,
  156.           play_arpeggio,play_bass,changed_difficulty,changed_guess,
  157.           chose_new_chord,redraw_inversion_check_box,need_to_redraw_erased_buttons,
  158.           play_previous,activate_and_deactivate;
  159.       GrafPtr save_graf;
  160.  
  161.       
  162.       need_to_redraw = 0;
  163.       need_to_redraw_all = 0;
  164.       ready_to_update = 0;
  165.       pressed_ok = 0;
  166.       play_it = 0;
  167.       they_gave_answer = 0;
  168.       cleared_scores = 0;
  169.       twiddle_check_box = 0;
  170.       twiddle_radio_buttons = 0;
  171.       play_arpeggio = 0;
  172.       play_bass = 0;
  173.       changed_difficulty = 0;
  174.       changed_guess = 0;
  175.       chose_new_chord = 0;
  176.       redraw_inversion_check_box = 0;
  177.       need_to_redraw_erased_buttons = 0;
  178.       activate_and_deactivate = 0;
  179.       play_previous = 0;
  180.       
  181.            
  182.       if (first_time) { //-- this happens the first time our window is created,
  183.                   // and also when it's been closed and recreated
  184.         int i,ii,ee,this_chord_item;
  185.         first_time = 0;
  186.         ntries = 0;
  187.         nright = 0;
  188.         current_icon = BLANK_ICON_ID;
  189.         chord_or_arpeggio = 1;
  190.         typical_tempo = TYPICAL_TEMPO_CHORD;
  191.         mean_note = 61;
  192.         raw_difficulty = 5;
  193.         difficulty = scale_difficulty(raw_difficulty);
  194.         min_notes = 3;
  195.         max_notes = 4;
  196.         inversions_allowed = 0;
  197.         they_have_chord_to_consider = 0;
  198.         have_previous_chord = 0;
  199.         made_about_chord_window = 0;
  200.         n_enabled = 0;
  201.               for (i=0; i<=MAXPART; i++) { //-- normally we break out of inside of loop
  202.         part_number_to_nifty_label(nifty_name,&nifty_index,*decoder_string,i);
  203.         if (nifty_index < 0) break;
  204.             this_chord_item = part_num_to_chord_item(i,*decoder_string);
  205.             if (this_chord_item!=0 && IS_CHECK_BOX(this_chord_item)) {
  206.               ii = PLAIN_CHORD_ITEM(this_chord_item);
  207.               if (ii<=HI_7TH_CHORD
  208.                       && ii!=CHORD_AUG_TRIAD
  209.                       && ii!=CHORD_SUS_TRIAD
  210.                       && ii!=CHORD_FLAT5_TRIAD
  211.                       ) {
  212.                 add_to_list(enabled_list,&n_enabled,ii);
  213.               }
  214.             }
  215.           }
  216.       }
  217.  
  218.       switch(my_complete_window->whassup) {
  219.         case complete_window_created:
  220.           need_to_redraw = 1;
  221.           need_to_redraw_all = 1;
  222.           break;
  223.         case complete_window_redraw:
  224.           need_to_redraw = 1;
  225.           need_to_redraw_all = 1;
  226.           ready_to_update = 1; //-- main program does begin update & sets graf port
  227.           break;
  228.         case complete_window_action:
  229.           part_number_to_nifty_label(nifty_name,&nifty_index,
  230.             *decoder_string,my_complete_window->part_number);
  231.           chord_item_pressed = part_num_to_chord_item(
  232.                   my_complete_window->part_number,*decoder_string);
  233.           if (nifty_index != -999) {
  234.             if (strcmp(nifty_name,"ok")==0) {
  235.               pressed_ok = 1;
  236.             }
  237.             if (strcmp(nifty_name,"play")==0) {
  238.               play_it = 1;
  239.             }
  240.             if (strcmp(nifty_name,"play_previous")==0) {
  241.               play_previous = 1;
  242.             }
  243.             if (strcmp(nifty_name,"wipe")==0) {
  244.           changed_guess = 1;
  245.           need_to_redraw = 1;
  246.           guessed_basic_part = 0;
  247.           activate_and_deactivate = 1;
  248.           n_guess = 0;
  249.             }
  250.             if (strcmp(nifty_name,"help")==0) {
  251.               make_chord_help_window();
  252.             }
  253.             if (strcmp(nifty_name,"play_arpeggio")==0) {
  254.               play_arpeggio = 1;
  255.               play_it = 1;
  256.             }
  257.             if (strcmp(nifty_name,"bass")==0) {
  258.               play_bass = 1;
  259.               play_it = 1;
  260.             }
  261.             if (strcmp(nifty_name,"cl")==0) {
  262.               ntries = 0;
  263.               nright = 0;
  264.               cleared_scores = 1;
  265.               need_to_redraw = 1;
  266.               current_icon = BLANK_ICON_ID;
  267.             }
  268.             if (strcmp(nifty_name,"higher_difficulty")==0) {
  269.               raw_difficulty += 1;
  270.               if (raw_difficulty>20) raw_difficulty=20;
  271.               difficulty = scale_difficulty(raw_difficulty);
  272.               changed_difficulty = 1;
  273.               need_to_redraw = 1;
  274.             }
  275.             if (strcmp(nifty_name,"lower_difficulty")==0) {
  276.               raw_difficulty -= 1;
  277.               if (raw_difficulty<1) raw_difficulty=1;
  278.               difficulty = scale_difficulty(raw_difficulty);
  279.               changed_difficulty = 1;
  280.               need_to_redraw = 1;
  281.             }
  282.             if (strcmp(nifty_name,"inversions")==0) {
  283.               need_to_redraw = 1;
  284.               redraw_inversion_check_box = 1;
  285.               inversions_allowed = !inversions_allowed;
  286.               if (!inversions_allowed 
  287.                   && is_in_list(enabled_list,n_enabled,CHORD_NO1)) {
  288.                 remove_from_list(enabled_list,&n_enabled,CHORD_NO1);
  289.             twiddle_check_box = 1;
  290.           }
  291.             }
  292.             if (strcmp(nifty_name,"cho")==0) {
  293.               if (chord_or_arpeggio==2)
  294.                 duration = duration * ((double) TYPICAL_TEMPO_ARPEGGIO)
  295.                 /((double) TYPICAL_TEMPO_CHORD);
  296.               chord_or_arpeggio = 1;
  297.               need_to_redraw = 1;
  298.               twiddle_radio_buttons = 1;
  299.               typical_tempo = TYPICAL_TEMPO_CHORD;
  300.             }
  301.             if (strcmp(nifty_name,"ar")==0) {
  302.               if (chord_or_arpeggio==1)
  303.                 duration = duration * ((double) TYPICAL_TEMPO_CHORD)
  304.                 /((double) TYPICAL_TEMPO_ARPEGGIO);
  305.               chord_or_arpeggio = 2;
  306.               need_to_redraw = 1;
  307.               twiddle_radio_buttons = 1;
  308.               typical_tempo = TYPICAL_TEMPO_ARPEGGIO;
  309.             }
  310.         if (chord_item_pressed != 0) {
  311.           if (IS_CHECK_BOX(chord_item_pressed)) { //-- check box
  312.             unsigned int blargh,wugga;
  313.             twiddle_check_box = 1;
  314.                 need_to_redraw = 1;
  315.             wugga = chord_item_pressed;
  316.             blargh = PLAIN_CHORD_ITEM(wugga);
  317.             if (is_in_list(enabled_list,n_enabled,blargh))
  318.               remove_from_list(enabled_list,&n_enabled,blargh);
  319.             else
  320.               add_to_list(enabled_list,&n_enabled,blargh);
  321.           }
  322.           else { //-- button
  323.             //-- the only thing to watch out for is if they
  324.             //        change their mind about the basic part of
  325.             //        the chord
  326.             if (chord_item_pressed<=HI_7TH_CHORD
  327.                  && guessed_basic_part) { //--replace basic part
  328.               for (i=0; i<n_guess; i++) {
  329.                 if (guess[i]<=HI_7TH_CHORD)
  330.                   remove_from_list(guess,&n_guess,guess[i]);
  331.               }
  332.             }
  333.             if (!is_in_list(guess,n_guess,chord_item_pressed)
  334.                 || chord_item_pressed<=HI_7TH_CHORD) {
  335.               add_to_list(guess,&n_guess,chord_item_pressed);
  336.               if (chord_item_pressed<=HI_7TH_CHORD) {
  337.                 guessed_basic_part = 1;
  338.                 activate_and_deactivate = 1;
  339.               }
  340.             }
  341.             else {
  342.               remove_from_list(guess,&n_guess,chord_item_pressed);
  343.             }
  344.             changed_guess = 1;
  345.             need_to_redraw = 1;
  346.           }
  347.         }
  348.           }//--end if they hit a valid control
  349.           break;
  350.         case complete_window_erase:
  351.           they_have_chord_to_consider = 0;
  352.           first_time = 1;
  353.           chord_window_exists = 0;
  354.           if (about_chord_window_exists && VALID_POINTER(where_is_about_chord_complete_window))
  355.             delete where_is_about_chord_complete_window;
  356.           return;
  357.       }
  358.  
  359.       if (pressed_ok && guessed_basic_part) {
  360.         int actual_chord[MAX_CHORD_NOTES],guess_chord[MAX_CHORD_NOTES];
  361.         int n_actual_notes,n_guess_notes;
  362.         
  363.         activate_and_deactivate = 1;  //-- now that they've pressed ok, dim it
  364.         guessed_basic_part = 0;    //      ...
  365.         
  366.         make_chord(actual_chord, &n_actual_notes,  item,  n_items);
  367.         make_chord(guess_chord,  &n_guess_notes,   guess, n_guess);
  368.         
  369.         right = same_canonical_form(actual_chord,n_actual_notes,
  370.                         guess_chord,n_guess_notes);
  371.                 //-- do they have the same canonical form?
  372.         
  373.         ++ntries;
  374.         nright += right;
  375.         if (right) {
  376.           current_icon = CHECK_ICON_ID;
  377.         }
  378.         else {
  379.           current_icon = X_ICON_ID;
  380.         }
  381.         they_have_chord_to_consider = 0;
  382.         they_gave_answer = 1;
  383.         need_to_redraw = 1;
  384.         if (!have_previous_chord && !made_about_chord_window) {
  385.           made_about_chord_window = 1;
  386.           make_about_chord_window();
  387.           SelectWindow(my_complete_window->the_window);
  388.               // bring attention back to me!
  389.         }
  390.         have_previous_chord = 1;
  391.         previous_root = root;
  392.         previous_n_notes = n_notes;
  393.         copy_chord(previous_chord,chord,n_notes);
  394.         if (right) {
  395.           //-- If they gave the right answer, that is what we
  396.           //    want to put down as the description of the previous
  397.           //    chord.  I.e., if they gave a valid alternative
  398.           //    description of the chord, we don't want them
  399.           //    to think they got it wrong.
  400.           int try_root,new_items[MAX_CHORD_ITEMS],new_n_items;
  401.           previous_n_items = n_guess;
  402.           copy_chord(previous_item,guess,n_guess);
  403.           //-- what root is implied by their guess?
  404.           for (try_root=0; try_root<=11; try_root++) {
  405.             analyze_chord_based_on_this_root(
  406.                 new_items,&new_n_items,chord,n_notes,try_root);
  407.             if (new_n_items!=0 && 
  408.                 lists_are_same_except_for_order(
  409.                     new_n_items,new_items,n_guess,guess)) {
  410.               previous_root = try_root;
  411.               break;
  412.             }
  413.           }
  414.         }
  415.         else {
  416.           previous_n_items = n_items;
  417.           copy_chord(previous_item,item,n_items);
  418.         }
  419.         //-- let about_chord_den_mother know it should redraw itself
  420.         if (about_chord_window_exists) {
  421.           GrafPtr save_graf;
  422.           WindowPtr window;
  423.           window = where_is_about_chord_complete_window->the_window;
  424.  
  425.           SelectWindow(window);
  426.  
  427.           GetPort(&save_graf);
  428.           SetPort(window);
  429.         
  430.           EraseRect(&window->portRect);
  431.           DrawControls(window);
  432.         
  433.           where_is_about_chord_complete_window->whassup
  434.              = complete_window_redraw;
  435.           (*(where_is_about_chord_complete_window->den_mother))
  436.             (where_is_about_chord_complete_window);
  437.         
  438.           SetPort(save_graf);
  439.           
  440.           SelectWindow(my_complete_window->the_window);
  441.               // bring attention back to me!
  442.         }
  443.       }
  444.  
  445.       if (need_to_redraw) {
  446.         if (!ready_to_update) {
  447.           GetPort(&save_graf);
  448.           SetPort(my_complete_window->the_window);
  449.         }
  450.         if (activate_and_deactivate || need_to_redraw_all) {
  451.           if (guessed_basic_part)
  452.             activate_ctl_by_nifty_label(my_complete_window->the_window,
  453.                 "ok",1,*decoder_string);
  454.           else
  455.             deactivate_ctl_by_nifty_label(my_complete_window->the_window,
  456.                 "ok",1,*decoder_string);
  457.           if (have_previous_chord)
  458.             activate_ctl_by_nifty_label(my_complete_window->the_window,
  459.                 "play_previous",1,*decoder_string);
  460.           else
  461.             deactivate_ctl_by_nifty_label(my_complete_window->the_window,
  462.                 "play_previous",1,*decoder_string);
  463.         }
  464.         if (twiddle_radio_buttons || need_to_redraw_all) {
  465.           set_ctl_by_nifty_label(my_complete_window->the_window,
  466.             "cho",1,*decoder_string,(chord_or_arpeggio==1));
  467.           set_ctl_by_nifty_label(my_complete_window->the_window,
  468.             "ar",1,*decoder_string,(chord_or_arpeggio==2));
  469.         }
  470.         if (twiddle_check_box || need_to_redraw_all) {
  471.           int yowza,this_chord_item,nifty_index;
  472.           char nifty_name[30];
  473.               for (yowza=0; yowza<=MAXPART; yowza++) { //-- normally we break out of inside of loop
  474.         part_number_to_nifty_label(nifty_name,&nifty_index,*decoder_string,yowza);
  475.         if (nifty_index < 0) break;
  476.             this_chord_item = part_num_to_chord_item(yowza,*decoder_string);
  477.             if (this_chord_item!=0 && IS_CHECK_BOX(this_chord_item)) {
  478.               set_ctl_by_nifty_label(my_complete_window->the_window,
  479.                      nifty_name,nifty_index,*decoder_string,
  480.                      is_in_list(enabled_list,n_enabled,
  481.                              PLAIN_CHORD_ITEM(this_chord_item)));
  482.             }
  483.           }
  484.         }
  485.         if (need_to_redraw_all || redraw_inversion_check_box) {
  486.           set_ctl_by_nifty_label(my_complete_window->the_window,
  487.                      "inversions",1,*decoder_string,inversions_allowed);
  488.         }
  489.         if (have_previous_chord 
  490.             && (need_to_redraw_all || they_gave_answer)) {
  491.           char s[200];
  492.           Handle h;
  493.           if (1) { //-- new method of showing right answer: guitar chord symbol
  494.             get_item_by_nifty_label(my_complete_window->the_window,
  495.                   "correct_answer",1,*decoder_string,&h);
  496.             if (VALID_HANDLE(h)) {
  497.               describe_chord(s+1,previous_n_items,previous_item);
  498.               s[0] = strlen(s+1);
  499.               TextSize((short) 18);
  500.               TextFont(geneva);
  501.               SetIText(h,(unsigned char *) s);
  502.           normal_text_style();
  503.             }
  504.           }
  505.         }
  506.         if (they_gave_answer && !right) {
  507.           if (0) {  //-- old method of showing right answer: erase buttons
  508.               int i,this_chord_item,nifty_index;
  509.               char nifty_name[30];
  510.               Handle h;
  511.               need_to_redraw_erased_buttons = 1;
  512.                   for (i=0; i<=MAXPART; i++) { //-- normally we break out of inside of loop
  513.             part_number_to_nifty_label(nifty_name,&nifty_index,*decoder_string,i);
  514.             if (nifty_index < 0) break;
  515.                 this_chord_item = part_num_to_chord_item(i,*decoder_string);
  516.                 if (this_chord_item!=0 && !IS_CHECK_BOX(this_chord_item)
  517.                     && !is_in_list(item,n_items,this_chord_item)) {
  518.                 hide_ctl_by_nifty_label(my_complete_window->the_window,
  519.                      nifty_name,nifty_index,*decoder_string);
  520.               }
  521.             }
  522.           }
  523.         }
  524.         if (need_to_redraw_all) {
  525.           normal_text_style();
  526.           refresh_text(my_complete_window,"t1",1,*decoder_string);
  527.           refresh_text(my_complete_window,"t2",1,*decoder_string);
  528.           refresh_text(my_complete_window,"t3",1,*decoder_string);
  529.           refresh_text(my_complete_window,"t4",1,*decoder_string);
  530.           refresh_text(my_complete_window,"t5",1,*decoder_string);
  531.           normal_text_style();
  532.         }
  533.         if (need_to_redraw_all || changed_difficulty) {
  534.           {
  535.             Handle h;
  536.             char s[50];
  537.             Rect r;
  538.             short x,y;
  539.             PolyHandle arrowhead;
  540.             get_item_by_nifty_label(my_complete_window->the_window,
  541.                   "d",1,*decoder_string,&h);
  542.             if (VALID_HANDLE(h)) {
  543.               sprintf(s+1,"difficulty: %d",(int) raw_difficulty);
  544.               s[0] = strlen(s+1);
  545.               SetIText(h,(unsigned char *) s);
  546.               refresh_text(my_complete_window,"d",1,*decoder_string);
  547.             }
  548.           }
  549.         }
  550.         if (they_gave_answer || cleared_scores || need_to_redraw_all) {
  551.           Handle h;
  552.           char s[50];
  553.           int i,tot;
  554.           get_item_by_nifty_label(my_complete_window->the_window,
  555.                   "sc",1,*decoder_string,&h);
  556.           if (VALID_HANDLE(h)) {
  557.             if (ntries>0) {
  558.               sprintf(s+1,"%d %c",(int) ((100.*nright)/((double) ntries)),'%');
  559.               s[0] = strlen(s+1);
  560.             }
  561.             else {
  562.               sprintf(s+1,"           ");
  563.               s[0] = strlen(s+1);
  564.             }
  565.             TextSize((short) 18);
  566.             TextFont(geneva);
  567.             SetIText(h,(unsigned char *) s);
  568.         normal_text_style();
  569.           }
  570.         }
  571.         if (they_gave_answer || cleared_scores || need_to_redraw_all) {
  572.           Handle hh;
  573.           Rect rr;
  574.           hh = GetIcon((short) current_icon);
  575.           if (VALID_HANDLE(hh)) {
  576.             get_rect_by_nifty_label(my_complete_window->the_window,
  577.                      "ic",1,*decoder_string, &rr);
  578.             PlotIcon(&rr,hh);
  579.           }
  580.         }
  581.         if (they_gave_answer && !right) {
  582.           int i,this_chord_item,nifty_index;
  583.           char nifty_name[30];
  584.           //-- old method of showing correct answer was to erase other buttons;
  585.           //-- now we have to redraw them:
  586.           if (need_to_redraw_erased_buttons) {
  587.               rest((int)( 2. * 2000.*60./typical_tempo));
  588.                   for (i=0; i<=MAXPART; i++) { //-- normally we break out of inside of loop
  589.             part_number_to_nifty_label(nifty_name,&nifty_index,*decoder_string,i);
  590.             if (nifty_index < 0) break;
  591.                 this_chord_item = part_num_to_chord_item(i,*decoder_string);
  592.                 if (this_chord_item!=0 && !IS_CHECK_BOX(this_chord_item)
  593.                         && !is_in_list(item,n_items,this_chord_item)) {
  594.                   show_ctl_by_nifty_label(my_complete_window->the_window,
  595.                          nifty_name,nifty_index,*decoder_string);
  596.                 }
  597.               }
  598.           }
  599.         }
  600.         if (!ready_to_update) {
  601.           SetPort(save_graf);
  602.         }
  603.       }//---end if need to redraw
  604.       if (!they_have_chord_to_consider) {
  605.         chose_new_chord = 1;
  606.         choose_chord(item,&n_items,difficulty,enabled_list,n_enabled,
  607.                       min_notes,max_notes);
  608.         make_chord(chord,&n_notes,item,n_items);
  609.         root = 0; //-- this gets changed below when we shift the
  610.                     // whole chord to its final pitch, and
  611.                     // also in case we reinterpret the chord
  612.         fiddle_with_chord_voicing(chord,n_notes,inversions_allowed);
  613.         //-- kludge: if it's a m7 with 3 in bass, reinterpret as a 6 chord
  614.         if (n_notes==4 && notes_to_quick_label(chord,n_notes)==2131) {
  615.           if (make_0_to_11(chord[0]-root)==3) {
  616.             n_items = 2;
  617.             item[0] = CHORD_MAJ_TRIAD;
  618.             item[1] = CHORD_6;
  619.             make_chord(chord,&n_notes,item,n_items);
  620.             root = 0;
  621.           }
  622.         }
  623.             center = mean_note+(random_double()-.5)*6+(random_double()-.5)*6;
  624.         avg = avg_chord(chord,n_notes);
  625.         for (i=0; i<n_notes; i++) {
  626.           chord[i] += center-avg;
  627.         }
  628.         root += center-avg;
  629.         duration = choose_duration(typical_tempo);
  630.         if (random_double()<.5) {
  631.           arpeggio_direction = 1;
  632.         }
  633.         else {
  634.           arpeggio_direction = -1;
  635.         }
  636.         play_it = 1;
  637.         they_have_chord_to_consider = 1;
  638.         n_guess = 0;
  639.         guessed_basic_part = 0;
  640.       }
  641.       if (play_it || play_previous) {
  642.         if (play_bass) {
  643.           play_chord(1,my_snd_chan,chord,duration,1);
  644.         }
  645.         else {
  646.                 int chord_to_play[MAX_CHORD_NOTES],play_n_notes;
  647.                 if (play_it) {
  648.                   copy_chord(chord_to_play,chord,n_notes);
  649.                   play_n_notes = n_notes;
  650.                 }
  651.                 else { //-- play previous chord
  652.                   copy_chord(chord_to_play,previous_chord,previous_n_notes);
  653.                   play_n_notes = previous_n_notes;
  654.                 }
  655.             if (chord_or_arpeggio==1 && !play_arpeggio) {
  656.               play_chord(play_n_notes,my_snd_chan,chord_to_play,duration,1);
  657.             }
  658.             else {
  659.               int i,x[4],d,start,stop;
  660.               if (chord_or_arpeggio==1) //--we're really set to play chords, so speed
  661.                               // up for arpeggio
  662.                 d = duration*((double) TYPICAL_TEMPO_CHORD )
  663.                     /((double) TYPICAL_TEMPO_ARPEGGIO);
  664.               else 
  665.                 d = duration;
  666.               if (arpeggio_direction>0) {
  667.                 start = 0;
  668.                 stop = play_n_notes-1;
  669.               }
  670.               else {
  671.                 start = play_n_notes-1;
  672.                 stop = 0;
  673.               }
  674.               i = start;
  675.               for (;;) {
  676.                 x[0] = chord_to_play[i];
  677.                 play_chord(1,my_snd_chan,x,d,1);
  678.                 if (i==stop) break;
  679.                 i += arpeggio_direction;
  680.               }
  681.             }
  682.         }
  683.       }
  684.  
  685.       //--- A second block of code for updating the window: the chord symbol
  686.       //    for their guess.  The reason it has to be down here is that
  687.       //    if we're moving on to the next chord, we wait until now to 
  688.       //    erase their previous guess, _after_ playing the new chord
  689.         if (chose_new_chord || changed_guess || need_to_redraw_all) {
  690.           Handle h;
  691.           char s[50];
  692.           int i;
  693.           if (!ready_to_update) {
  694.             GetPort(&save_graf);
  695.             SetPort(my_complete_window->the_window);
  696.           }
  697.           get_item_by_nifty_label(my_complete_window->the_window,
  698.                   "guess",1,*decoder_string,&h);
  699.           if (VALID_HANDLE(h)) {
  700.             describe_chord(s+1,n_guess,guess);
  701.             s[0] = strlen(s+1);
  702.             TextSize((short) 18);
  703.             TextFont(geneva);
  704.             SetIText(h,(unsigned char *) s);
  705.             normal_text_style();
  706.           }
  707.           if (!ready_to_update) {
  708.             SetPort(save_graf);
  709.           }
  710.         }
  711.  
  712.  
  713.  
  714.     }
  715.  
  716.     double
  717. scale_difficulty(int raw)
  718.     {
  719.       double z,y;
  720.       z = raw;
  721.       if (z>20.) z=20.;
  722.       y = .3*z*z+z-1.;
  723.       if (z>=10.) y += 1000.*((1./(21.-z))-(1./11.));
  724.       return y;
  725.     }
  726.  
  727.     unsigned int
  728. plain_chord_item(unsigned int x)
  729.     {
  730.       unsigned int y;
  731.       y = 0x7FFFL;
  732.       return x & y;
  733.     }
  734.  
  735.     int
  736. part_num_to_chord_item(int part,char *decoder_string)
  737.     {
  738.       char nifty_name[50];
  739.       int nifty_index;
  740.       part_number_to_nifty_label(nifty_name,&nifty_index,decoder_string,part);
  741.       if (strcmp(nifty_name,"majt")==0)     return CHORD_MAJ_TRIAD;
  742.       if (strcmp(nifty_name,"mint")==0)     return CHORD_MIN_TRIAD;
  743.       if (strcmp(nifty_name,"dimt")==0)     return CHORD_DIM_TRIAD;
  744.       if (strcmp(nifty_name,"augt")==0)     return CHORD_AUG_TRIAD;
  745.       if (strcmp(nifty_name,"sust")==0)     return CHORD_SUS_TRIAD;
  746.       if (strcmp(nifty_name,"b5t")==0)      return CHORD_FLAT5_TRIAD;
  747.       if (strcmp(nifty_name,"7")==0)      return CHORD_DOM7;
  748.       if (strcmp(nifty_name,"m7")==0)      return CHORD_MIN7;
  749.       if (strcmp(nifty_name,"maj7")==0)      return CHORD_MAJ7;
  750.       if (strcmp(nifty_name,"o7")==0)     return CHORD_DIM7;
  751.       if (strcmp(nifty_name,"h")==0)      return CHORD_HALF_DIM;
  752.  
  753.       if (strcmp(nifty_name,"b9")==0)      return CHORD_FLAT9;
  754.       if (strcmp(nifty_name,"9")==0)      return CHORD_9;
  755.       if (strcmp(nifty_name,"#9")==0)      return CHORD_SHARP9;
  756.       if (strcmp(nifty_name,"11")==0)      return CHORD_11;
  757.       if (strcmp(nifty_name,"#11")==0)      return CHORD_SHARP11;
  758.       if (strcmp(nifty_name,"b6")==0)      return CHORD_FLAT6;
  759.       if (strcmp(nifty_name,"6")==0)      return CHORD_6;
  760.       if (strcmp(nifty_name,"a7")==0)      return CHORD_ADD7;
  761.       if (strcmp(nifty_name,"amaj7")==0)      return CHORD_ADDMAJ7;
  762.       
  763.       if (strcmp(nifty_name,"no1")==0)      return CHORD_NO1;
  764.       if (strcmp(nifty_name,"no3")==0)      return CHORD_NO3;
  765.       if (strcmp(nifty_name,"no5")==0)      return CHORD_NO5;
  766.       
  767.       if (strcmp(nifty_name,"cmajt")==0)     return CHORD_MAJ_TRIAD | 0x8000;
  768.       if (strcmp(nifty_name,"cmint")==0)     return CHORD_MIN_TRIAD | 0x8000;
  769.       if (strcmp(nifty_name,"cdimt")==0)     return CHORD_DIM_TRIAD | 0x8000;
  770.       if (strcmp(nifty_name,"caugt")==0)     return CHORD_AUG_TRIAD | 0x8000;
  771.       if (strcmp(nifty_name,"csust")==0)     return CHORD_SUS_TRIAD | 0x8000;
  772.       if (strcmp(nifty_name,"cb5t")==0)      return CHORD_FLAT5_TRIAD | 0x8000;
  773.       if (strcmp(nifty_name,"c7")==0)      return CHORD_DOM7 | 0x8000;
  774.       if (strcmp(nifty_name,"cm7")==0)      return CHORD_MIN7 | 0x8000;
  775.       if (strcmp(nifty_name,"cmaj7")==0)      return CHORD_MAJ7 | 0x8000;
  776.       if (strcmp(nifty_name,"co7")==0)     return CHORD_DIM7 | 0x8000;
  777.       if (strcmp(nifty_name,"ch")==0)     return CHORD_HALF_DIM | 0x8000;
  778.       
  779.       if (strcmp(nifty_name,"cb9")==0)      return CHORD_FLAT9 | 0x8000;
  780.       if (strcmp(nifty_name,"c9")==0)     return CHORD_9 | 0x8000;
  781.       if (strcmp(nifty_name,"c#9")==0)      return CHORD_SHARP9 | 0x8000;
  782.       if (strcmp(nifty_name,"c11")==0)      return CHORD_11 | 0x8000;
  783.       if (strcmp(nifty_name,"c#11")==0)     return CHORD_SHARP11 | 0x8000;
  784.       if (strcmp(nifty_name,"cb6")==0)      return CHORD_FLAT6 | 0x8000;
  785.       if (strcmp(nifty_name,"c6")==0)      return CHORD_6 | 0x8000;
  786.       if (strcmp(nifty_name,"ca7")==0)      return CHORD_ADD7 | 0x8000;
  787.       if (strcmp(nifty_name,"camaj7")==0)     return CHORD_ADDMAJ7 | 0x8000;
  788.  
  789.       if (strcmp(nifty_name,"cno1")==0)      return CHORD_NO1 | 0x8000;
  790.       if (strcmp(nifty_name,"cno3")==0)      return CHORD_NO3 | 0x8000;
  791.       if (strcmp(nifty_name,"cno5")==0)      return CHORD_NO5 | 0x8000;
  792.       
  793.       return 0;
  794.     }
  795.  
  796.     //
  797.     //  Play with chord voicing.
  798.     // If inversions_allowed is false, leave root in bass.  Assume chord[0] is root.
  799.     // Chord is always returned as a sorted list, lowest note first.
  800.     //
  801.     void
  802. fiddle_with_chord_voicing(int *chord,int n,int inversions_allowed)
  803.     {
  804.       int i,lowest,height_above_bass,height_above_lower_neighbor,
  805.             height_below_upper_neighbor,nearest_neighbor,z,
  806.           changed_voicing,j,min_height;
  807.       int used_up[MAX_CHORD_NOTES];
  808.       if (inversions_allowed)
  809.         lowest = 0;
  810.       else
  811.         lowest = 1;
  812.       do {
  813.         for (j=0; j<n; j++) {
  814.           used_up[j] = 0;
  815.         }
  816.         for (j=lowest; j<n; j++) {
  817.           do {
  818.             i = -1 + lowest + random_integer(n-lowest);
  819.           } while(used_up[i]);
  820.           used_up[i] = 1;
  821.  
  822.             changed_voicing = 0;
  823.             if (i>0) {
  824.               height_above_lower_neighbor = chord[i]-chord[i-1];
  825.               height_above_bass = chord[i]-chord[0];
  826.             }
  827.             else {
  828.               height_above_lower_neighbor = 0;
  829.               height_above_bass = 999;
  830.             }
  831.             if (i<n-1)
  832.               height_below_upper_neighbor = chord[i+1]-chord[i];
  833.             else
  834.               height_below_upper_neighbor = 999;
  835.             if (height_below_upper_neighbor<height_above_lower_neighbor)
  836.               nearest_neighbor=height_below_upper_neighbor;
  837.             else
  838.               nearest_neighbor=height_above_lower_neighbor;
  839.             if (12.+random_double()*6.<height_above_lower_neighbor) {
  840.               chord[i] -= 12;
  841.               changed_voicing = 1;
  842.             }
  843.             else {
  844.               if (nearest_neighbor<3 && random_double()*3.>nearest_neighbor
  845.                       && random_double()<.5) {
  846.                 chord[i] += 12;
  847.                 changed_voicing = 1;
  848.               }
  849.               else {
  850.                 switch(random_integer(3)) {
  851.                   case 1: min_height = 0; break;
  852.                   case 2: min_height = 3; break;
  853.                   case 3: min_height = 5; break;
  854.                 }
  855.                 if (height_above_bass<=min_height) {
  856.                   chord[i] += 12;
  857.                   changed_voicing = 1;
  858.                 }
  859.               }
  860.             }
  861.             if (changed_voicing)
  862.               sort_int_list(chord,n);
  863.  
  864.  
  865.         }
  866.       } while(random_double()<.3);
  867.     }
  868.     
  869.     // large difficulty (say 999) means all chords equally likely
  870.     // difficulty of 7 means strangest chords 7 times less likely than most normal
  871.     // Note that what we are passed is the _scaled_ difficulty.
  872.     void
  873. choose_chord(int *item,int *n,double difficulty,int *enabled_list,int n_enabled,
  874.         int min_notes,int max_notes)
  875.     {
  876.       int z,what,chord[MAX_CHORD_NOTES],new_chord[MAX_CHORD_NOTES],
  877.               n_notes,diss,new_n_notes,seal_of_approval,
  878.               save_items[MAX_CHORD_NOTES],save_n_items,bogus,i,
  879.               qqq;
  880.      
  881.       do { //--- outer loop checks if simplification gives something they disabled
  882.       
  883.       do { //--- until we find a chord that's not too dissonant
  884.      
  885.           //---- first, choose the basic chord quality         
  886.           do {
  887.               do {
  888.                 z = random_double()*13.;
  889.               } while (z<1 || z>11);
  890.               switch(z) {
  891.                 case 1: what = CHORD_MAJ_TRIAD; break;
  892.                 case 2: what = CHORD_MIN_TRIAD; break;
  893.                 case 3: what = CHORD_DIM_TRIAD; break;
  894.                 case 4: what = CHORD_AUG_TRIAD; break;
  895.                 case 5: what = CHORD_SUS_TRIAD; break;
  896.                 case 6: what = CHORD_FLAT5_TRIAD; break;
  897.                 case 7: what = CHORD_DOM7; break;
  898.                 case 8: what = CHORD_MIN7; break;
  899.                 case 9: what = CHORD_MAJ7; break;
  900.                 case 10: what = CHORD_DIM7; break;
  901.                 case 11: what = CHORD_HALF_DIM; break;
  902.               }
  903.           }while (
  904.                 (random_double()*difficulty+1.<how_unusual_is_basic_chord(what)
  905.                   && random_double()<.8)
  906.               || !is_in_list(enabled_list,n_enabled,what) );
  907.           item[0] = what;
  908.           *n = 1;
  909.           make_chord(chord,&n_notes,item,*n);
  910.           
  911.           //---- now, choose some added tones; use plenty, because it
  912.           //    can always get rejected later for being too dissonant
  913.           while (random_double()<.9 && random_double()*(.3*difficulty+1.2)>1.
  914.                   && n_notes<=max_notes+3) {
  915.             do {
  916.               do {
  917.                 z = random_double()*14.;
  918.               } while (z<1 || z>12);
  919.               switch(z) {
  920.                   case 1: what = CHORD_FLAT9; break;
  921.                   case 2: what = CHORD_9; break;
  922.                   case 3: what = CHORD_SHARP9; break;
  923.                   case 4: what = CHORD_11; break;
  924.                   case 5: what = CHORD_SHARP11; break;
  925.                   case 6: what = CHORD_FLAT6; break;
  926.                   case 7: what = CHORD_6; break;
  927.                   case 8: what = CHORD_ADD7; break;
  928.                   case 9: what = CHORD_ADDMAJ7; break;
  929.                   case 10: what = CHORD_NO1; break;
  930.                   case 11: what = CHORD_NO3; break;
  931.                   case 12: what = CHORD_NO5; break;
  932.               }//--end switch
  933.               make_chord(chord,&n_notes,item,*n);
  934.               new_n_notes = n_notes;
  935.               if (!is_in_list(item,*n,what)) {
  936.                 add_to_list(item,n,what);
  937.                 make_chord(new_chord,&new_n_notes,item,*n);
  938.                 remove_from_list(item,n,what);
  939.               }
  940.               diss = dissonance(new_chord,new_n_notes);
  941.               seal_of_approval = 
  942.                   !(
  943.                         random_double()*difficulty<how_unusual_is_added_tone(what)
  944.                     || !is_in_list(enabled_list,n_enabled,what)
  945.                     || new_n_notes==n_notes
  946.                   );
  947.             } while (!seal_of_approval 
  948.                     && random_double()>.05     //--make sure it doesn't loop forever
  949.                 );
  950.                     //--while loop for picking one that's not too unusual
  951.             if (seal_of_approval) {
  952.               add_to_list(item,n,what);
  953.               make_chord(chord,&n_notes,item,*n);
  954.             }
  955.             else
  956.               break;
  957.           }//--while loop for adding one tone after another
  958.           
  959.           //-- Special cases for combinations of items that don't really
  960.           //    make sense:
  961.           bogus = (*n==2 && item[1]==CHORD_NO1)
  962.                       //... without root, becomes a triad
  963.               || (*n==2 && item[0]==CHORD_DIM7 && item[1]>=LO_OMITTED_TONE
  964.                   && item[1]<=HI_OMITTED_TONE)
  965.                       //...=dim triad
  966.               || (*n==2 && item[0]==CHORD_HALF_DIM && item[1]==CHORD_NO3)
  967.                       //... =b5 triad
  968.                   ;
  969.           
  970.           make_chord(chord,&n_notes,item,*n);
  971.           diss = dissonance(chord,n_notes);
  972.             qqq = random_double()*difficulty;
  973.             while (random_double()<.2) qqq += 1.; //-- avoid infinite loop on low
  974.                                 // difficulty
  975.       } while(qqq<2.*diss-1.
  976.               || bogus || n_notes<min_notes || n_notes>max_notes);
  977.           //--end outer while loop; if too dissonant, try again
  978.           
  979.       //
  980.       // Now simplify the chord.  But if simplification results in a
  981.       // reduction of number of items by one or less, and the simplified
  982.       // version uses disabled items, go back to the more complicated
  983.       // version.
  984.       //
  985.       
  986.       copy_chord(save_items,item,*n);
  987.       save_n_items = *n;
  988.                 
  989.       simplify_chord(item,n);
  990.       
  991.       bogus = 0;
  992.       for (i=0; i<*n; i++) {
  993.         bogus = bogus || !is_in_list(enabled_list,n_enabled,item[i]);
  994.       }
  995.       
  996.       if (bogus && *n>=save_n_items-1) {
  997.         bogus = 0;
  998.         copy_chord(item,save_items,save_n_items);
  999.         *n = save_n_items;
  1000.       }
  1001.       
  1002.       } while (bogus);
  1003.     }
  1004.  
  1005.  
  1006.     void
  1007. make_about_chord_window()
  1008.     {
  1009.       int previous_n_windows;
  1010.       if (!about_chord_window_exists) {
  1011.         previous_n_windows = n_windows;
  1012.         make_window(ABOUT_CHORD_DLOG_ID,"\p","\p","\p","\p",
  1013.               &about_chord_den_mother);
  1014.         if (n_windows==previous_n_windows+1) {
  1015.           where_is_about_chord_complete_window = my_windows[n_windows-1];
  1016.           about_chord_window_exists = 1;
  1017.         }
  1018.         else {
  1019.           where_is_about_chord_complete_window = (complete_window *) 0;
  1020.           about_chord_window_exists = 0;
  1021.         }
  1022.       }
  1023.     }
  1024.  
  1025.  
  1026.  
  1027.  
  1028.  
  1029.  
  1030.